home *** CD-ROM | disk | FTP | other *** search
/ Freaks Macintosh Archive / Freaks Macintosh Archive.bin / Freaks Macintosh Archives / Textfiles / zines / DNA / DNAV1I9.sit / DNAV1I9 / DNA109.013 < prev    next >
Text File  |  1994-08-24  |  37KB  |  740 lines

  1.                  _         _/ \_             _/ \_          _
  2.                _/ \_      /  _  \__        _/  _  \_      _/ \_
  3.              _/  _  \_______/ \  | \_      |  / \ _______/  _  \_
  4.             /  _/ \         \_ | |\_ \_    | |  _/         / \_  \
  5.             \ /    | |----\_  \_ |  \_ \_  | |_/  _/----| |    \ /
  6.                    | |      \_  \|    \_ \_| /   /      | |
  7.                    | |     _/   ||        \||    \_     | |
  8.                    | |   _/   _/ |         | \_    \_   | |
  9.             / \_   | |__/   _/ | |         | | \_    \__| |   _/ \
  10.             \_  \_/  ______/\_/  |         |  \_/\_______  \_/  _/
  11.               \_   _/     \_   _/           \_   _/      \_   _/
  12.                 \_/         \_/               \_/          \_/
  13.                        
  14.                        
  15.                        Assembly Tutorial 1
  16.  
  17.      Welcome back to DNA magazine! We've been out of publication
  18. for quite a few months now, and I've had plenty of time to write
  19. *really* long articles on my favorite subjects. I've decided to
  20. write this article in hopes that some of our readers will try and
  21. learn more about a very powerful and rewarding language. I of
  22. course am speaking of assembly language. Assembly is a very
  23. difficult language to grasp, but one which is very rewarding to the
  24. programmer who understands it. Knowledge of assembly language will
  25. give you a lot more insight into how upper level languages handle
  26. different situations. This knowledge will help you make more
  27. efficient and bug free code in everything you do, in and out of
  28. assembly. I'm not writing this as a tutorial as how to write a
  29. virus, or how to crack software. My opinion on those is that you
  30. have to learn how to write your own programs before you can
  31. reprogram someone elses. Both virii and cracking are very difficult
  32. subjects, and I won't get into them at all in this document. There
  33. is more than enough to talk about without even touching on those.
  34. You've been warned: this text contains constructive information
  35. only!
  36.      If you've ever looked at assembly langauge source code, you'll
  37. realize that there's just a whole list of commands all looking
  38. exactly the same. This tremendously long string of similar
  39. instructions are in a seemingly random sequence. Yet there are
  40. those who claim to understand this cryptic language, and create
  41. very fast, and sometimes very impressive applications with it. What
  42. I plan to teach you are some of the more basic, but very useful
  43. applications of assembly language.
  44.  
  45.                         LANGUAGE OVERVIEW
  46.  
  47.         For those of you not familiar with programming languages,
  48. I will go over the basics of what led up to modern day programming.
  49. Really though, if you don't know any programming languages,
  50. assembly is NOT the first on you should attempt to master. In that
  51. light, this is presented as and overview only. First of all, when
  52. the first computers were created, there were neccesarily, no
  53. programming languages. This presented quite a challenge for those
  54. who wanted to do something with their new expensive computer. The
  55. computer doesn't speak english; quite to the contrary, it speaks a
  56. binary language that humans are not familiar with, and is difficult
  57. to translate. In order to program an early CPU, one had to write
  58. everything in binary and send it individually to the CPU for
  59. execution. This first level of language is called machine language
  60. or machine code. It's absolutely garbage to look at. None of it
  61. makes any sense. If you've ever looked at the hex dump of an
  62. executable program, you know what I'm talking about. The next step
  63. up was the creation of assembly language, which introduced nmemonic
  64. operations to represent the machine code instructions and labels
  65. for the jumps in code. While this may sound foreign to you, that's
  66. OK, I'll explain it later on. The next invention was that of high
  67. level languages which kept the programmer out of the low level
  68. aspects of program design. In C for instance, the programmer never
  69. knows what value is in a register, and doesn't really care. He can
  70. code things with impunity and expect the compiler to create the
  71. machine code to do what he asks. There are other classifications
  72. for languages, but it's not really important. To become a good
  73. programmer, you must understand the three that I've outlined,
  74. machine code, assembly language and high level languages. You'll
  75. need to know how to code proficiently in the latter two.
  76.  
  77.                        CHOOSING A LANGUAGE
  78.  
  79.      Rarely, if ever will you have to program anything in machine
  80. code directly. Once in a while you will have to throw a machine
  81. code instruction into your programs in assembly. Typically, you
  82. don't want to code an entire application in assembly. High level
  83. languages handle most of the work associated with I/O on the
  84. computer, with much better precision than you will in assembly. I'm
  85. sure you've heard that assembly language is the fastest, and most
  86. compact langauage there is. While this is true, but you must also
  87. consider development time when you choose your language. Writing
  88. all of some large application in assembly is a task no one should
  89. have to take on. Functions such as taking input from the user and
  90. writing info to  the disk can all be done just as efficiently with
  91. a high level language. It will take you 20 times less writing time
  92. to make a high level language do what you want, and it will achieve
  93. the same result as a homemade assembly routine. Therefore, the
  94. structure of your code is most efficiently maintained in some high
  95. language. My personal preference is C, but whichever one you prefer
  96. is fine by me. C is just the most straight forward for introducing
  97. assembly code to. I'll get into interfacing at a later time.
  98.  
  99.  
  100.                          80x86 INTERNALS
  101.  
  102.      Now that we've got the introductions out of the way, I would
  103. like to start on the basics of Intel x86 based machines. First of
  104. all, the smallest unit you can address directly on an Intel machine
  105. is a byte. A byte is a series of 8 bits in a sequence. It is
  106. possible to change just a single bit, but not directly. Each bit
  107. can have a value of one or zero. If an individual bit is a one, it
  108. is said to be 'set'; if it's a zero, it's said to be 'cleared.' The
  109. next size up from the byte is a word, which is two bytes end to
  110. end. A word is 16 bits, rather than 8. The next larger common size
  111. is a double word, which is 2 words, or 4 bytes, or 32 bits. Rarely
  112. is anything adressed in increments of more than 32 bits. For
  113. floating point calculations, there are larger storage locations,
  114. but they are beyond the scope of this introduction. Intel machines
  115. are bizarre in a number os ways. Not the least of which is how it
  116. stores values in memory. As it turns out, the Intel line of
  117. processors don't actually store words or doublewords in the same
  118. order you would expect. The processor, when it stores the data in
  119. memory or in a register actually stores it in byte reversed format,
  120. often called little-endian. This isn't a big problem when you're
  121. writing code, since you never see directly where a variable is
  122. stored, but you simply need to be aware of this, particularly when
  123. you're debugging code. A memory dump in your debugger will look
  124. pretty strange to you if you don't remember that the values are
  125. stored in byte reversed format. What does this mean? Let's say you
  126. want to store a 16 bit(word) value to memory. You write it to
  127. memory, and take a look at a memory dump. Rather than being written
  128. in a row like this(separated into two bytes with pipe symbol):
  129.  
  130. 0 1 2 3 4 5 6 7  |  8 9 10 11 12 13 14 15
  131.  
  132. In fact, it's written like this:
  133.  
  134. 8 9 10 11 12 14 15  |  0 1 2 3 4 5 6 7
  135.  
  136. Pretty weird way of doing things, but apparently Intel had a
  137. *really* good reason for doing this. It's never been explained
  138. adequately to me however. I chalk that up as just one in a
  139. tremendous string of Intel quirks.
  140.  
  141.                    NUMBERING SYSTEMS AND BASES
  142.  
  143.      Understanding different bases is essential to good assembly
  144. language programming. Humans are very used to a decimal numbering
  145. system. It is more specifically referred to as base ten. This means
  146. that there are ten distinct elements in each place of a decimal
  147. number. These elements are the numerals zero through nine. Two
  148. other common bases are used in programming: binary and hexidecimal.
  149. Binary is what the computer uses at the very lowest level to
  150. represent all information. Binary digits have only two distinct
  151. numerals: zero and one. It looks like this: 100100011111... This is
  152. what the computer uses, but it's nearly useless to humans. If I
  153. were to ask you to tell me what that number above were in base 10,
  154. you'd be very hard pressed to tell me in under thirty seconds.
  155. That's why the hexidecimal numbering system is used. It's the step
  156. inbetween decimal and binary. Hexidecimal is base 16. It was
  157. chosen, since everything is done in increments of 8 bits(one byte)
  158. on intel processors. Each byte can be represented by a two digit
  159. hexidecimal number. A two digit hex number can have the values zero
  160. through 256, just like an 8 bit binary digit(one byte) can.
  161. Hexidecimal has 16 individual elements defined as zero through nine
  162. and 'A' through 'F'. 'A' and 'F' are the decimal values 10 and 15,
  163. respectively. The letters inbetween are the values inbetween 10 and
  164. 15. True to all numbering systems, the places of a hexidecimal
  165. digit go up in factors of the base of itself. This may sound
  166. confusing, but it's not. The first digit of any numbering system is
  167. always the ones digit. From there on out, you multiply the previous
  168. value by the base(in this case hexidecimal: 16) to find the values
  169. of the successive decimal places. In a hex number, the first digit
  170. is the one's place, the second the 16's place, the next is the
  171. 256's place and so on. On a binary number, the places are
  172. successive factors of two. The first place is always a one, the
  173. second two, the third four, the fourth eight  and so on. The same
  174. is true of decimal numbers, but you probably know how to read them
  175. already. To read a hex or binary number, you need to multiply each
  176. digit by it's place value. Let's do a sample hex value: 3E87h.
  177. Typically, hex values are followed by an 'h'. It's done simply to
  178. help you from getting confused about which base the number is. It
  179. can be either upper or lower case, but the latter is more common.
  180. Let's pull that number apart with their places:
  181. -------------------------------------
  182. Place: 4096    256         16       1
  183. -------------------------------------
  184. Digit: 3          E       8           7
  185.      3*4096    14*256    8*16       7*1
  186. -------------------------------------
  187.      12288     3584       128       7   =    16007
  188.  
  189. Reading and writing Hex numbers is an important part of learning
  190. assembly. Much of debugging of code requires that you convert
  191. between bases. So if you want to learn assembly, be sure to get
  192. this subject down.
  193.  
  194.                   REGISTERS, MEMORY AND STORAGE
  195.  
  196.      In order to do anything important a CPU has to be able to
  197. store data somewhere. There are a number of places that data can be
  198. stored. The most simple to access are in the CPU itself, called
  199. registers. The general purpose registers on the 16 bit x86 machines
  200. are labeled  'AX', 'BX', 'CX', and 'DX'. These are the four
  201. registers that you will use the most often. There are others which
  202. tell you and the processor where to look for certain pieces of
  203. information. Registers are the fastest place to read and write
  204. information from, since the CPU doesn't have to call any external
  205. components in your computer in order to read to them or write to
  206. them. The registers have other important uses in calling DOS and
  207. BIOS functions. Typically, they are used to send data to and
  208. receive data from these functions. For instance, you would put data
  209. into the AX register that the DOS or BIOS function would then use
  210. to do some function, then return a value in another register. You
  211. could then test the data, and depending on it's results change the
  212. flow of your program.
  213.      Then next level of storage is base memory. The memory resides
  214. off the actual CPU, which means to access it, your application has
  215. to pull info off the data bus, which makes access substantially
  216. slower than that of registers. Intel machines have another quirk in
  217. how they access memory; they use a segmented structure. In standard
  218. 16bit mode, which is all that DOS convieniently supports, memory is
  219. addressed using a 16bit segment and a 16bit offset. In programs,
  220. you denote a memory location in the form 'segment:offset'. You can
  221. use data or registers to point to the memory you want to use. For
  222. instance, you can look at '1234:5678' as a memory location, or you
  223. can use a register for one or both the segment and offset('1234:BX'
  224. addresses the memory at segment 1234 and the offset of the value
  225. currently in the BX register). To access adjacent memory locations,
  226. you simply need to increment or decrement the offset by one. Adding
  227. one to the offset always takes you to the next byte of memory in a
  228. particular segment. Let's say you wanted to write data to five
  229. memory locations, which all happen to be in a row, like a string.
  230. You would first put the data you want to write to the memory
  231. locations in a register. Then you would move that data to your
  232. first offset, say: 'xxxx:0000'(It really doesn't matter what
  233. segment you are in, this works for all  cases). Then to write the
  234. piece of data to the next location, you would then write to
  235. 'xxxx:0001'. Typically, you don't have to explicity name which
  236. segment you want to draw info from, because they are implied
  237. depending on what kind of an operation you are doing. There are
  238. four segments you can address at any given time in any program. To
  239. reach them, you have to tell the CPU where they are in memory. In
  240. order to do this, you have the four segment registers loaded with
  241. the address of the four segments. The four registers are named CS,
  242. DS, ES, and SS. They stand for Code, Data, Extra, and Stack
  243. segments. They are self explanatory. They code segment is where the
  244. code resides that you want the processor to run. The data segment
  245. is where you store all your data variables. The extra segment
  246. doesn't have any designated job. It has a variety of uses which we
  247. will discuss later. The stack segment is where all the stack data
  248. is stored. The stack is a specialized data segment which we will
  249. also discussed later. When you do an operation that is associated
  250. with a given segment, you don't have to tell the processor which
  251. segment you want it to access. It knows implicitly which one to
  252. draw from. For instance, if you say:
  253. MOV         AX,variable
  254. You don't have to tell it you want to draw that information from
  255. the data segment. The processor knows you want to pull a variable
  256. from data. You are allowed to over-ride this default however. If
  257. you have data stored in the ES, you can get at it thusly:
  258. MOV         AX,ES:variable
  259.  
  260. The CPU itself doesn't actually use this segmented convention. When
  261. you send the processor the segment and offset, it converts that
  262. into a 20 bit physical memory location. This being the case, in 16
  263. bit standard mode, the maximum amount of memory that the processor
  264. can directly access is 1,048,576(2^20) bytes of memory. This is
  265. where the DOS memory limit comes from. With the advent of 32 bit
  266. processors, the limit was drastically expanded to around 64
  267. terabytes. Unfortunately, DOS, doesn't support 32 bit programs
  268. particularly well; so you're stuck writing 16 bit programs until a
  269. solution is devised(Windoze 4.0) Rest assured that the segmented
  270. memory usage of the Intel line of processors is one of the most
  271. difficult aspect of the machine to master. If you are confused
  272. about segments, don't feel alone: thousands of people have been
  273. cursing Intel for choosing segmentation for years.
  274.      Permanent memory is different from registers and memory in
  275. that it retains the info stored on it after the computer has been
  276. turned off or rebooted. Hard drives, floppies, CDROMS, tape backups
  277. and the like are all permanent memory. Assembly code access to
  278. these objects is often difficult and obscure. Reading from a hard
  279. drive and the like is done much more easily in high level
  280. languages, so I won't talk about it here. If you obsolutely need to
  281. access permanent memory, find a book that talks about DOS
  282. interrupts to do so.
  283.  
  284.      Now that we have an general understanding of memory on Intel
  285. machines, let's look in more detail at CPU registers. Compared to
  286. other machines(The new PowerPC chips have around 30 general purpose
  287. registers!), Intel processors have relatively few regs. I've
  288. mentioned the general registers: AX,BX,CX,and DX. Each is slightly
  289. specialized for doing a certain task, but are still flexible enough
  290. to allow generalized work. Their pseudonyms are Accumulator, Base,
  291. Counter, and Data registers. Each of these four general purpose
  292. registers can also be addressed one byte at a time. Each of the
  293. registers can store two bytes, so there is an upper and a lower
  294. byte in each. To address only a single byte, the registers are
  295. split like this: AX is split into AL and AH for the lower and
  296. higher bytes. The BX into BL and BH. The CX into CL and CH. The DX
  297. into DL and DH. The next four registers are the segment
  298. registers(CS,DS,ES,SS) which point to the memory location of their
  299. respective segments. There are three pointer registers named:
  300. BP,IP, and SP. The BP(base pointer) is not used much, but is
  301. associated with the stack. You'll find that high level languages
  302. use the base pointer a lot, but that you can generally ignore it.
  303. The IP(instruction pointer) tells the processor which instruction
  304. it is to execute next in the code segment. Each time an instruction
  305. is loaded into the processor, it increments the IP to point to the
  306. next instruction. The SP is the Stack Pointer. It tell the
  307. processor where the top of the stack is at in the stack segment.
  308. The last two registers are the SI(source index) and DI(destination
  309. index) registers which are often used in string operations. They
  310. are also used in indirect memory addressing, as shown above. There
  311. is actually one more register on an 8086, called the flags
  312. register. The flag register is used to detect certain things in the
  313. code. This register holds information temporarily after a
  314. comparison has been made, for instance. When you compare the value
  315. in a memory location with a number; if they are equal, the zero
  316. flag is set. The zero flag(ZF) is a single bit in the flags
  317. register. Other operations check to see the status of that flag,
  318. and change the course of the code appropriately.
  319.  
  320.                      MOVING DATA WITH 'MOV'
  321.  
  322.      How is data moved around with assembly language? The most
  323. common instruction by far is the 'MOV' instruction. Despite being
  324. the pseudonym for move, it in fact copies data from one location to
  325. another. The MOV instruction takes two arguments, one of which must
  326. be a register. The other can be either another register, memory, or
  327. an immediate. An immediate is data you actually hard code into your
  328. source. If you ask the computer to put a 5 into the AX register,
  329. the 5 is an immediate. The fact that one argument must be a
  330. register means you can't simply move data from one memory location
  331. to another with this instruction, you have to use a register as a
  332. middleman. First you must move the data into a register and then
  333. move that data in the register into the new memory location. Here
  334. are some examples of how to use the MOV instruction:
  335.  
  336. MOV     Destination , Source     ;General form
  337. MOV         AX,5                                              ;Put the value 5 into AX
  338. MOV         AX,CX                                          ;Copy the data in CX to AX
  339. MOV         BX,memory_location     ;Copy data from memory to BX
  340.  
  341. The name, 'memory_location' above was a variable that was declared
  342. earlier in the program. You can access variables in many of the
  343. same ways you can in higher level languages. Just like most
  344. languages except basic, you do have to declare your variables
  345. explicitly. With assembly language there are a variety of ways to
  346. address the variables however. The first one, which  you way above
  347. is called direct addressing. When assembled the code will call a
  348. specific memory location, and take the data directly out of it to
  349. put into the BX register. Direct addressing is the simplest form of
  350. addressing except for immediate. The other form of addressing is
  351. indirect, which is similar to using pointers in C. By putting a
  352. register in brackets, you mean to say that you want to access the
  353. memory pointed to by the contents of the register(similar to the *
  354. indirection operator in terms of pointers in C). Here is a simple
  355. example of indirect addressing:
  356.  
  357. MOV         BX,offset memory_location
  358. MOV         AX,[BX]
  359.  
  360. This example puts the offset of the desired variable into BX, then
  361. uses the indirection operator to load the address pointed to in BX
  362. into AX. The end result is that the value stored in the variable
  363. 'memory_location' is put into AX.
  364.  
  365. There are other more powerful methods of indirect addressing. The
  366. one above is called register indirect addressing. The other modes
  367. are 'based'(or indexed) and 'based and indexed with displacement.'
  368. Examples follow:
  369.  
  370. MOV         AX,[BX]       ;Register indirect
  371. MOV         AX,[BX+2]    ;Displaced
  372. MOV         AX,[BX+SI+2] ;Based and indexed with displacement
  373.  
  374. All are valid ways of addressing memory. You can also turn those
  375. around to move data from the register into memory:
  376.  
  377. MOV         [BX],AX
  378. MOV         [BX+2],AX
  379. MOV         [BX+SI+2],AX
  380.  
  381. The last of the examples above([BX+SI+2]) simply adds the values in
  382. BX and SI and adds two, then stores the data pointed to by that
  383. value in the AX register. These are all difficult to understand
  384. concepts, and you won't have to use them until you start trying to
  385. solve very difficult problems in assembly.
  386.  
  387.                 SIMPLE INSTRUCTIONS: OR, AND, XOR
  388.  
  389.      Now that we've seen the basic ways of moving data around in
  390. various storage locations in the computer, we can start exploring
  391. ways of manipulating data to get useful results from it. Some of
  392. the simplest data manipulation instructions are the bitwise
  393. operators: OR, AND, and XOR. If you've used pascal, these are not
  394. the same as the similar instructions there. They work on the bit
  395. level to change the value of a single byte, word or dword. For
  396. instance, if you have the decimal value 170, which is 10101010
  397. binary, and you AND that with 00001111, you will simply cut off the
  398. top 4 bits of the original number like so:
  399.  
  400. 10101010
  401. AND
  402. 00001111
  403. --------
  404. 00001010
  405.  
  406. AND works by checking each bit in the source byte with the second
  407. byte and checking if both bits are set in each position. If, and
  408. only if both bits are set, does the destination bit get set. This
  409. is a very handy function to know. This means that if you want to
  410. test whether an individual is set, all you have to do is use an
  411. 'AND mask.' The mask is the bit values you use to get rid of excess
  412. information. You simply set a bit in the mask if you want to test
  413. that particular bit. For instance, if you want to test if bit
  414. number seven to check if it's set, you would use the mask:
  415. 10000000, since bits are ordered from right to left, starting at
  416. zero.
  417.  
  418.  
  419. XXXXXXXX
  420. AND
  421. 10000000
  422. --------
  423. X0000000
  424.  
  425. Only the value of the bit we don't mask comes through the AND. We
  426. can then test whether the resultant byte is a zero or not. If it's
  427. a zero, we know the orginal bit 7 was a zero. If it's greater than
  428. zero, then bit 7 was a one. The OR function works very similarly:
  429.  
  430. 10101010
  431. OR
  432. 00001111
  433. --------
  434. 10101111
  435.  
  436. This simply works by checking to see if either one or both the bits
  437. in a certain spot are a one, and if they are, the resultant bit is
  438. also set to a one. Only if both the bits are zero will a zero be
  439. the answer. It's useful if you want to ensure that all the bits
  440. from the source byte get into the destination, perhaps with some
  441. additional bits set as well.
  442.  
  443. The last of the logical operators is the XOR expression. This one
  444. operates similarly to the OR operator except that the resulting bit
  445. is set if and only if a single bit of the two compared expressions
  446. is set. This example explains:
  447.  
  448. 11110000
  449. XOR
  450. 10101010
  451. --------
  452. 01011010
  453.  
  454. If and only if there is a difference between the two bits will the
  455. resultant bit be set to a one. You will often find this used in
  456. extremely simple encryption algorithms. If you use a certain bit
  457. pattern to XOR a string of text, XORing it again with the same
  458. pattern will return the original text. In the example above, XORing
  459. the resulting byte with the original pattern, '10101010' will
  460. return the original byte:
  461.  
  462. 01011010   :Result from above
  463. XOR
  464. 10101010   :Original pattern from above
  465. --------
  466. 11110000   :Original byte from above
  467.  
  468. The three operators above are extremely important to remember,
  469. because you will use them all the time with assembly to mask and
  470. set bits.
  471.  
  472.                             SHL, SHR
  473.  
  474.      These two other bitwise instructions come in quite handy as
  475. well. These are SHL and SHR. They stand for Shift Left and Shift
  476. Right. These operators are used to move a series of bits in the
  477. direction indicated(either left or right). The shift expression
  478. requires two arguments for use. The first one is the register which
  479. you wish to shift, and the second argument is how many bits you
  480. wish to shift it. This is an example:
  481.  
  482. MOV         AL,10101010 (binary)
  483. SHR         AL,1
  484. --------------
  485. 10101010 -->
  486. --------------
  487. 01010101
  488.  
  489. You can see that it simply shifts the bits to the right, and fills
  490. in the opening in the 8th bit with a zero. As it turns out, you
  491. can't specify a shift of more than one bit at one time with
  492. immediates:
  493.  
  494. SHL         AX,3    ;Doesn't run on pre-286 machines!
  495.  
  496. This is a no-no on Pre-286 machines. You can only tell the
  497. processor one shift at a time using immediates:
  498.  
  499. SHL         AX,1
  500. SHL         AX,1
  501. SHL         AX,1
  502.  
  503. This does the same thing as the above example, and it has the added
  504. benefit that it will actually run without crashing your computer
  505. old XT's. While you can't use immediates, there is a way to tell
  506. the computer to shift more than one bit at a time: using the CL
  507. register. You set the number of times you want a register shifted
  508. in CL. Then you tell the processor to shift a register CL times in
  509. whichever direction you choose:
  510.  
  511. MOV         CL,3
  512. SHL         AX,CL
  513.  
  514. I don't know why this restriction was placed on this command, but
  515. Intel does seem to do some 'unique' things; so I just accept it.
  516. Once you move on to programming in higher level processors, you can
  517. ignore this quirk. As it is, you can probably program in 286 mode,
  518. since very few users have 8086's any more. Make sure you include
  519. code which detects the processor, so it doesn't just crash on 8086
  520. machines. Keep in mind that if you are using a shareware assembler,
  521. such at A86, you can only program in 8086 mode. If you're using
  522. TASM on the other hand, you can get around many of the quirks of
  523. the 8086 processor by programming in 286 mode or above. Back to
  524. programming! The shifting operators have an interesting effect upon
  525. the register values they modify. When you shift a register to the
  526. left one place, you multiply the value inside it by two. This
  527. occurs for every bit you shift. Shifting 3 bits to the right
  528. multiplies the value by 8(2*2*2). Shifting to the right has the
  529. opposite effect. It will divide the register's contents by two for
  530. every bit. This is a very handy function to use, since it is much
  531. faster than actually using the multiplications operations provided
  532. with the Intel machines. If you know you're going to multiply
  533. something by a binary value: by all means use a shift instead of a
  534. multiply. The multiply operation on the Intel machines is much
  535. slower than simply shifting. Typically, shifting even multiple
  536. places takes only a single clock cycle to complete on 386 class
  537. machines(thanks to the barrel shifter). The multiply instruction,
  538. on the other hand, takes somewhere in the neighborhood of 30 clock
  539. cycles to complete. Coding in assembly means you want to write the
  540. fastest smallest code possible. Using this technique will help you
  541. dramatically. I use this technique in my graphics programs, which
  542. I will show later.
  543.  
  544.                                MUL
  545.  
  546.      Now that I've covered a limited type of multiplication, I'll
  547. go into detail with the Intel general purpose unsigned
  548. multiplication instruction. The fact that it's unsigned means you
  549. can't use it if you want to be able to include negative values in
  550. your work. You will find relatively limited use for negative values
  551. anyway, so you should find this is adequate. If you want signed
  552. multiplying, look at the IMUL instruction(It's more complicated,
  553. since you have to know two's complement notation binary encoding).
  554. The original MUL instruction takes input in the form of either a
  555. byte or a word. The 386 instruction allows for doubleword
  556. multipliers. With an 8086, you always multiply a memory location
  557. and either the AL or AX registers. When the data from the multiply
  558. is stored, it is stored in twice the amount of space of the two
  559. multipliers. If you multiply together two words, the maximum result
  560. is 64k, which you cannot store in only 8 bits. Therefore, it stores
  561. it in a word(specifically, the AX register). If you give the
  562. instruction two word size multipliers, it stores it in the combo
  563. 'DX:AX', with the AX register holding the lower 16 bits of the
  564. answer. If the answer fits in the same amount of space as the
  565. multipliers, the Carry Flag(CF), and the Overflow Flag(OF) are both
  566. cleared. If the answer spills into the upper half of the answer
  567. area, both the flags are set. In english? If you multiply together
  568. two byte size numbers, and the answer can also fit into a byte, the
  569. CF and OF are clear. Otherwise, the flags are set. In the case of
  570. the word operands, if the answer can be stored completely in the AX
  571. register, then the two flags are clear. If the answer also requires
  572. that you read the DX register, the flags are set. There are two
  573. forms of the instruction you can use. One of the multipiers is
  574. always the AL or AX register, and the other can be either a general
  575. purpose register(besides AX), or a memory location. Let's do some
  576. examples:
  577.  
  578. MOV         AL,3
  579. MOV         BL,3
  580. MUL         BL
  581. JC           large
  582.  
  583. This example
  584.  
  585.  
  586.  
  587.  
  588.                        LODSx, MOVSx, STOSx
  589.      There are two versions of LODSx, MOVSx, and STOSx on the early
  590. Intel machines, and a third one was added with the advent of the
  591. 386. Replace the 'x' in the above opcodes with either a 'b' for
  592. byte, a 'w' for word, or 'd' for dword(on 386 and above). Now let's
  593. get to what they do. The LODSx instruction is an acronym for Load
  594. String Byte(or Word, or Dword). This instruction looks to see what
  595. the registers DS:SI point to, and load that value into the AL
  596. register. If You use the LODSW, it load the info into the full AX
  597. register. Using the LODSD requires you have a 386 or above, and you
  598. have to use the 32 bit extended registers. I won't get into this,
  599. because there is simply too much to talk about. If there is a
  600. request for more info, I'd be happy to write a 386 assembly
  601. tutorial. Once the information is loaded into the register, the SI
  602. register is incremented by the size of the data was you loaded(1
  603. for byte, 2 for word, 4 for dword). This makes this series of
  604. instructions handy for doing repeated loads, since it reads in all
  605. the information in a segment in a row, if put into a looping
  606. statement. The MOVSB instruction is the only way to move data from
  607. one memory location to another directly. You of course can use the
  608. standard MOV instruction, but you have to use a middleman register
  609. like this:
  610.  
  611. MOV                             AX,memory_loc_1
  612. MOV         memory_loc_2,AX
  613.  
  614. The MOVSB instruction is a very handy way of moving large chunks of
  615. data from one place to another location. It takes no arguments. It
  616. looks at the registers DS:SI, and moves the data pointed to there
  617. into ES:DI. It also increments the values of SI and DI by the
  618. length of the data moved(1 for byte, 2 for word, 4 for dword). This
  619. makes this a quick way of moving data, since once the move is
  620. started, you can keep repeating the same instruction to move large
  621. chunks of memory. Since you have to set up several registers, this
  622. method may look complicated, but it is truly the fastest way to
  623. transfer information in any language. Once set up, you can put it
  624. into a loop to move as much as a whole segment(64k) of memory
  625. anywhere you want. It will take about .00262144 seconds on a 386-
  626. 25. For instance, to write to a color screen in text mode, you must
  627. write to the segment '0B800h'. This is some sample code to write a
  628. single letter to the first position on the screen(upper left
  629. corner):
  630.  
  631. MOV         AX,@DATA; Should be done already.
  632. MOV         DS,AX   ; Quirk: can't MOV immediates to segment regs
  633. MOV         AX,0B800h; Assumes you have a color monitor
  634. MOV         ES,AX   ; Segment registers are set up
  635. MOV         SI,offset string_variable
  636. MOV         DI,0
  637. MOVSB
  638.  
  639. Let's examine what this code does. First, it loads the appropriate
  640. values into the segment registers. It puts the segment where your
  641. variables are stored into DS. Then it moves 0B800h into ES, which
  642. is the segment where the screen is written to. You may have noticed
  643. you can't simply move immediate data into segment registers. This
  644. is just another cute little quirk Intel provided for your
  645. entertainment. Now that the segments are set, you need to set the
  646. offsets for the reading and writing. You point to the string you
  647. want to print to the screen by putting it's offset into SI. Then,
  648. you point to the beginning of the screen by setting DI to zero.
  649. Now, the stosb instruction is invoked. It looks at DS:SI, which is
  650. the segment and offset of your string you want printed. It moves
  651. the first byte of it to ES:DI, which is the screen segment and the
  652. offset of the first position on the screen. The STOSB instruction
  653. is the opposite of the LODSB insruction. STOSB puts the byte in the
  654. AL register into the spot pointed to by ES:DI. Then it increments
  655. DI by the length of the data stored.
  656.  
  657.      There are plenty more instructions you can learn, but with
  658. just the few that I've described here, you can start writing
  659. assembly language routines which do useful work.
  660.  
  661.                             STRUCTURE
  662.  
  663.      Assembly language is a very simple language structurally.
  664. There is a single indentation pattern for the whole code, unlike
  665. most high level languages like C:
  666.  
  667. if ( a == b ) then
  668.   {
  669.   ...
  670.   }
  671.  
  672. You indent the code differently depending on what part of the code
  673. you're in. With assembly, it's all written linearly:
  674.  
  675.      MOV         AX,3
  676.      MOV         BX,4
  677.      AND         AX,BX
  678.      OR           AX,7
  679.  
  680. With no jumping instructions, assembly routines execute in the
  681. order they are written. In order to control the flow of execution,
  682. jumping commands have to be put into the code. Using jump
  683. statements means you probably want to compare something first, then
  684. change the flow of your program depending on the results. Comparing
  685. data is typically done with the CMP instruction. Here is some
  686. sample code:
  687.  
  688.      MOV         AX,3
  689.      MOV         BX,4
  690.      CMP         AX,BX
  691.      JA           above
  692.      JB           below
  693.      JE           equal
  694.  
  695. This example compares the AX(3) to BX(4), and jumps to the
  696. appropriate location. In this case, it will jump to the code marked
  697. by below, since AX is below the value of BX. The locations are
  698. marked elsewhere in code on their own line followed by a colon,
  699. like this:
  700.  
  701. loophere:
  702.      MOV         AX,3
  703.      MOV         BX,8
  704.      DEC         BX       ;Subtracts one from BX
  705.      CMP         BX,AX
  706.      JA           loophere;JA = Jump if above
  707.  
  708. This code sets AX to 3 and BX to 8. Then, it subtracts one from BX
  709. with the DEC instruction, and checks if BX is larger than AX still.
  710. If it is, it goes back through the loop again. It will repeat until
  711. BX has the value of 3. There are lots of jump instructions, but
  712. here are the most common and useful:
  713.  
  714. JE   =    Jump if equal
  715. JA   =    Jump if above
  716. JB   =    Jump if below
  717. JC   =    Jump if carry flag set
  718. JMP  =    Jump unconditionally
  719.  
  720.      I'm afraid this is about all I can provide in one mag article.
  721. There's lots more to talk about, I haven't even covered a small
  722. portion of the instructions available to you. Basically, the only
  723. way you're ever going to learn assembly is if you try writing
  724. programs. Often when starting out, you'll need assistance. This is
  725. what this article is for. Hopefully you got an idea of what is
  726. needed to write assembly language programs. If anyone wants me to
  727. write more on this in later editions of DnA, let me know. I'll
  728. certainly be running other articles on programming, and if there's
  729. any particular subject you want covered, I'd be more than happy to
  730. write on it. I did a graphics programming tutorial this month, so
  731. check it out if you're interested. I hope you learned something, I
  732. sure did. I love to get feedback, and talk programming with fellow
  733. coders, so please drop me a line if you have questions, ideas,
  734. flames, criticisms, or any other matter you wish to discuss. I'm
  735. reachable at Digital Decay only right now. My E-mail account got
  736. shut down for over-use of other accounts, but I'll have it back
  737. soon I hope. Until then, have a day!
  738.  
  739. Zephyr [Cosys - Digital Decay] August 12, 1994
  740.